<template>
  <div>
    <div id="ui_top_panel" class="top_panel">
      <!-- 导航栏 -->
      <div class="toolbar">
        <ul>
          <li>
            <toggle-button
              v-model="isVetical"
              :width="60"
              :height="30"
              :disabled=true
              :sync="true"
              :labels="{checked: '纵向', unchecked: '横向'}"
            />
          </li>
          <li id="toolbar_zoom_out" style="margin-left: 2em;" title="缩小" @click="zoom_out()">&minus;</li>
          <li id="toolbar_zoom_in" title="放大" @click="zoom_in()">&plus;</li>
          <li>
            <el-input-number v-model="num_degree" :precision="1"
                             :step="0.5" :min="-89" :max="89" label="旋转角" step-strictly
                             size="small" :disabled="!can_change_degree">
            </el-input-number>
          </li>
        </ul>
      </div> <!-- endof #toolbar -->
    </div> <!-- endof #top_panel -->
    <!-- 中部：包括左侧工具栏和图像显示区 左边框250 -->
    <div class="middle_panel">
      <div id="leftsidebar" class="leftSizeBar" style="height: 100%;">
        <div>
          <el-card class="box-card">
            <div slot="header" class="clearfix">
              <span>图片详情</span>
            </div>
            <!--            <div v-for="o in 4" :key="o" class="text item">-->
            <!--              {{'列表内容 ' + o }}-->
            <!--            </div>-->
            <div v-if="!is_loading_current_image" class="text item box-card">
              <div>
                <span>图片名:{{ image_name }}</span>
              </div>
              <div>
                <span>图片状态:{{ image_status }}</span>
              </div>
              <div>
                <span>框数量:{{ num_box }}</span>
              </div>
              <div>
                <span>所属任务:{{ task_name }}</span>
              </div>
              <div>
                <span>被分配人员:{{ user_name }}</span>
              </div>
            </div>
          </el-card>
        </div>
        <div id="leftAll" style="height: 100%">
          <el-card class="box-card">
            <div id="leftTree" slot="header" style="height: 100%" class="clearfix">
              <span>目录树</span>
            </div>
            <div id="Tree" class="tree_box">
              <el-tree
                ref="vuetree"
                :data="node_data"
                node-key="id"
                highlight-current
                default-expand-all
                :expand-on-click-node="false"
              >
                <span
                  slot-scope="{ node, data }"
                  style="width: 100%"
                  class="custom-tree-node"
                  @click="() => show_node(node, data)"
                >
                  <span :style="getDetectStyle(node.data.need_detect)">
                    {{ node.label }}  {{ node.data.label_idx }}
                  </span>
                </span>
              </el-tree>
            </div>
          </el-card>
        </div>
      </div> <!-- end of leftsidebar -->
      <!-- 主显示区：图像画布canvas -->
      <div id="display_area" v-loading="is_loading_current_image">
        <div id="canvas_panel" @mousewheel.prevent="handleWheel">
          <VueDragResize
            ref="vueDR"
            :is-resizable="false"
            :is-draggable="isDragActive"
            :w="1"
            :h="1"
            @dragging="onDragging"
          >
            <canvas id="image_canvas" class="mainSizeBar" :style="{cursor: 'pointer'}"/>
            <canvas id="region_canvas" class="mainSizeBar" :style="getStyle()"/>
          </VueDragResize>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import jasonVueExplorer from './explorer'
import {getImageShow} from '../../../api/commonService'
import VueDragResize from 'vue-drag-resize'
import {passJudge} from '@/api/task/taskcheck'

class ImageMetadata {
  constructor(fileref, filename, size, fileid, result_path) {
    this.fileid = fileid
    this.filename = filename
    this.size = size
    this.result_path = result_path
    this.isVetical = true
    this.fileref = fileref // image url or local file ref.图片的详细信息
    this.regions = [] // 区域
    this.base64_img_data = '' // image data stored as base 64
    this.node_tree = [] // 目录树
  }
}

class ImageRegion {
  constructor() {
    this.node_id = 0 // 记录其在目录树节点的id
    this.is_user_selected = false // 默认为不选中
    this.shape_attributes = new Map() // 存该区域的坐标
  }
}

export default {
  name: 'image_show',
  components: {
    jasonVueExplorer,
    VueDragResize
  },
  data() {
    return {
      image_name: '',
      num_box: 0,
      image_status: '未分配',
      task_name: '',
      user_name: '',
      image_path: '',  //标示现在所展示的图片的地址
      base64_img_data: '',
      num_degree: 0,  //当前框的旋转角
      can_change_degree: false,
      leftTree: '',
      leftAll: '',
      tree: '',
      // rootPath: 'D:\\',
      currentPath: '',
      pathStack: [],
      dataArr: [],
      selection: true,
      seletedFileList: [],
      // imageDetailList:[], //存储被选中图片的所有信息 根据seletedFileList中的信息做扩展 base64 width height  size
      isVetical: true,
      nid: 0, // 目录树的id
      isDragActive: true,
      node_data: [],
      current_node: {},
      current_parent_node: {},
      can_draw_region: false, // 是否能画图
      REGION_SHAPE: { // 标注区域的形状
        RECT: 'rect' // 矩形
      },
      REGION_EDGE_TOL: 5, // pixel，误差范围之内就算点击到框边缘
      REGION_CONTROL_POINT_SIZE: 2,
      REGION_POINT_RADIUS: 3,
      POLYGON_VERTEX_MATCH_TOL: 5,
      REGION_MIN_DIM: 3, // 如果画的框太小
      MOUSE_CLICK_TOL: 2, // 移动超过2px就算移动
      CANVAS_DEFAULT_ZOOM_LEVEL_INDEX: 3, // 指代1
      CANVAS_ZOOM_LEVELS: [0.25, 0.5, 0.75, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.5, 4, 4.5, 5], // 缩放的尺度

      THEME_REGION_BOUNDARY_WIDTH: 2,
      THEME_BOUNDARY_LINE_COLOR: '#1a1a1a',
      THEME_BOUNDARY_FILL_COLOR: '#ff0000',
      THEME_SEL_REGION_FILL_COLOR: '#a2a8a2',
      THEME_SEL_REGION_FILL_BOUNDARY_COLOR: '#000000',
      THEME_SEL_REGION_OPACITY: 0.5,
      THEME_CONTROL_POINT_COLOR: '#ff0000',

      img_count: 0, // count of the loaded images
      canvas_regions: [], // 当前图片窗口中canvas的坐标 image regions spec. in canvas space
      canvas_scale: 1.0, // 原图缩小到canvas的倍数 current scale of canvas image

      image_id: '', // id:{filename+length} of current image
      image_index: -1, // 当前正在浏览的图片索引index

      current_image_filename: '',
      current_image: '',
      current_image_width: 0,
      current_image_height: 0,

      // image canvas
      img_canvas: '',
      img_ctx: '',
      reg_canvas: '',
      reg_ctx: '',
      canvas_width: 0,
      canvas_height: 0,

      // canvas zoom
      canvas_zoom_level_index: 3, // 1.0
      canvas_scale_without_zoom: 1.0,

      // state of the application
      is_user_drawing_region: false,
      current_image_loaded: false, // 图片是否在加载中
      is_window_resized: false,
      is_user_resizing_region: false,
      is_user_moving_region: false,
      is_user_drawing_polygon: false,
      is_region_selected: false,
      is_all_region_selected: false,
      is_user_updating_attribute_name: false,
      is_user_updating_attribute_value: false,
      is_user_adding_attribute_name: false,
      is_canvas_zoomed: false,
      is_loading_current_image: false, // 是否正在加载图片(导入图片的时候会用)
      is_region_boundary_visible: true,

      // region
      current_shape: '',
      user_sel_region_id: -1,
      click_x0: 0,
      click_y0: 0,
      click_x1: 0,
      click_y1: 0,
      region_click_x: 0,
      region_click_y: 0,
      region_edge: [-1, -1],
      current_x: 0,
      current_y: 0,

      // UI html elements
      ui_top_panel: '',
      canvas_panel: '',

      BBOX_LINE_WIDTH: 4,
      BBOX_SELECTED_OPACITY: 0.3,
      BBOX_BOUNDARY_FILL_COLOR_ANNOTATED: '#f2f2f2',
      BBOX_BOUNDARY_FILL_COLOR_NEW: '#aaeeff',
      BBOX_BOUNDARY_LINE_COLOR: '#1a1a1a',
      BBOX_SELECTED_FILL_COLOR: '#ffffff',

      is_ctrl_pressed: false,
      remote_node_data: [], // 从后端获取的当前图片的node_data
      remote_is_vetical: true, // 从后端获取的当前图片的状态
      remote_regions: [], // 远程node中读出的region
      max_id: 0 // 远程的最大id
    }
  },

  activated() {
    console.log('image_show页面activate')
    if (this.$store.state.imageshow.flag === true && this.$route.query.image_path !== this.$store.state.imageshow.image_path) {
      console.log('点击了不同的图片，清空之前的数据')
      Object.assign(this.$data, this.$options.data.call(this))
      this.get_init()
      this.$store.commit('SET_IMGAESHOW_FLAG_FALSE')
      this.$store.commit('SET_IMAGE_PATH', this.$route.query.image_path)
      this.clear_reg_canvas()
      this.getCheckedImage() // 获取图片和对应json文件
    }
  },
  created() {
    console.log('image_show页面create')
  },
  mounted() {
    console.log('image_show页面mount')
    this.get_init()
    const that = this
    // window.addEventListener('keydown', this.keydown)
    // user clicks on the canvas
    this.reg_canvas.addEventListener('mousedown', function (e) {
      that.click_x0 = e.offsetX // 相对于region_canvas的坐标
      that.click_y0 = e.offsetY
      // console.log('鼠标按下:', that.click_x0, that.click_y0)
      that.region_edge = that.is_on_region_corner(that.click_x0, that.click_y0) // 点击到框的四角上
      const region_id = that.is_inside_region(that.click_x0, that.click_y0)
      // 如果鼠标按到了已经选中的区域，可以移动或者调整区域大小
      // 如果按到了没有选中的区域或者本身没有区域被选中，则判定为在画框
      if (region_id === -1) { // 点击到别的区域，判定为在画框
        // unselect all regions
        that.is_region_selected = false
        that.toggle_all_regions_selection(false)
        that.redraw_reg_canvas()
      }
      e.preventDefault()
    }, false)
    // // implements the following functionalities:
    // //  - new region drawing (including polygon)
    // //  - moving/resizing/select/unselect existing region
    this.reg_canvas.addEventListener('mouseup', function (e) {
      that.click_x1 = e.offsetX
      that.click_y1 = e.offsetY
      // console.log('鼠标抬起', that.click_x1, that.click_y1)
      const click_dx = Math.abs(that.click_x1 - that.click_x0) // 移动的距离
      const click_dy = Math.abs(that.click_y1 - that.click_y0)
      // denotes a single click (= mouse down + mouse up)单击一个区域，即选中,记录选中区域的id，并且将对应的isselect标记为true
      if (click_dx < that.MOUSE_CLICK_TOL || click_dy < that.MOUSE_CLICK_TOL) {
        // console.log('鼠标按下移动距离过短，判定为单击')
        const region_id = that.is_inside_region(that.click_x0, that.click_y0)
        if (region_id >= 0) {
          // first click selects region
          // console.log('选中框id：', region_id)
          that.user_sel_region_id = region_id
          that.is_region_selected = true
          that.is_user_moving_region = false
          that.is_user_drawing_region = false
          that.can_draw_region = false
          that.num_degree = that.canvas_regions[region_id].shape_attributes.get('degree')  //读取角度
          // 将目录树的节点设置为高亮状态
          const select_node_id = that.canvas_regions[region_id].node_id
          // console.log('选中框的region_id：', select_node_id)
          that.$refs['vuetree'].setCurrentKey()
          that.$refs['vuetree'].setCurrentKey(select_node_id)
          that.selectCamera(select_node_id)
          // de-select all other regions if the user has not pressed Shift
          if (!e.shiftKey) {
            that.toggle_all_regions_selection(false)
          }
          that.set_region_select_state(region_id, true)
          // show_message('Click and drag to move or resize the selected region');
        } else {
          console.log('没有框被选中,取消所有框的选择状态')
          that.is_region_selected = false
          that.use_sel_region_id = -1
          that.$refs['vuetree'].setCurrentKey()  //清空目录树的选中状态
          that.toggle_all_regions_selection(false)
          if (that.is_user_drawing_region) {  //如果没有区域被选中，鼠标按下移动的距离又很小，则全部设置为未选中
            // clear all region selection
            that.is_user_drawing_region = false
          }
        }
        // console.log('取消画框')
        that.redraw_reg_canvas()
        that.reg_canvas.focus()
      }
    })
  },
  deactivated() {
    console.log('deactivated')
    window.removeEventListener('keydown', this.keydown)
  },
  beforeDestroy() {
    console.log('beforeDestroy')
    window.removeEventListener('keydown', this.keydown)
    this.$store.commit('SET_IMAGE_PATH', '')
  },
  methods: {
    selectCamera(select_node_id) {

      //这次选中的节点
      let currentNode = this.$refs.vuetree.getNode(select_node_id)
      console.log(currentNode)
      let openNode = currentNode
      let sum = 0
      while (openNode.parent !== null) {
        openNode.parent.expanded = true
        openNode = openNode.parent
      }
      //同级
      let all = 0

      let count1 = currentNode.data.label_idx
      // console.log("同级", count1)

      if (currentNode.parent.parent.parent === null) {

      } else {
        currentNode = currentNode.parent
        all = all + count1
      }

      let num = 0
      let num1 = 0
      let label = currentNode.parent.data.label
      let index = currentNode.parent.data.label_idx
      // console.log(label,index)

      let Node = currentNode.parent.parent
      // console.log("第二级")
      // console.log("最顶级",Node)
      let flag = 0
      let beforeCount = 0
      let currentCount = 0
      for (let i = 0; i < Node.childNodes.length; i++) {

        if (Node.childNodes[i].data.label === label && Node.childNodes[i].data.label_idx === index) {
          beforeCount = beforeCount + flag
          // console.log("beforeCount",beforeCount)
          let label1 = currentNode.data.label
          let index1 = currentNode.data.label_idx
          let Node3 = Node.childNodes[i]
          for (let i = 0; i < Node3.childNodes.length; i++) {
            if (Node3.childNodes[i].data.label === label1 && Node3.childNodes[i].data.label_idx === index1) {
              currentCount = currentCount + i
              // console.log("currentCount",currentCount)
              break;
            } else {
              let Node1 = Node3.childNodes[i]
              // console.log("222Node1",Node1)
              if (Node1.expanded === false) {
              } else {
                let sum = 0
                for (let i = 0; i < Node1.childNodes.length; i++) {
                  let Node2 = Node1.childNodes[i]
                  if (Node2.expanded === true) {
                    sum = sum + Node2.childNodes.length + 1
                    // console.log("2if",sum)
                  } else {
                    sum = sum + 1
                    // console.log("2else",sum)
                  }
                }
                num1 = num1 + sum
                // console.log("2sum",sum)
              }
              currentCount = num1
              // console.log("2num",num1)
            }
          }
          break;
        } else {

          let Node1 = Node.childNodes[i]
          if (Node1.expanded === false) {
            num = num + 1
          } else {
            let sum = 0
            for (let i = 0; i < Node1.childNodes.length; i++) {
              let Node2 = Node1.childNodes[i]
              if (Node2.expanded === true) {
                sum = sum + Node2.childNodes.length + 1
                console.log("Node2 sum", sum)
              } else {
                sum = sum + 1
                console.log("Node2 sum", sum)
              }
              console.log("else 内", sum)
            }
            num = num + sum
          }
          beforeCount = num
          console.log("num", num)


          // console.log("Node1",Node1)
        }
        flag = i + 1
        // console.log("index1",flag)
      }
      all = all + beforeCount + currentCount + 1
      // console.log("all",all)
      let dom = document.querySelector("#Tree")
      let dom1 = document.querySelector("#Tree1")
      //一个节点的高度是26px,这个值能否从对象中获取，等我把这个功能做好再考虑吧
      let nodeHight = 26
      // dom1.style.height = (all * nodeHight) + "px"
      // 13是因为，div高度为700像素，nodeHight*27=702，略超过700，
      // 又因为，div高度是700像素，要使被选中的节点显示在div可视区域的中间，
      // 所以就要将27折半，后一半节点要参与滚动条的位移
      // 计算位移距离时，减350的原因也是一样，要使被选中的节点显示在div可视区域的中间，就要减去div高度的一半
      if (9 < all) {
        dom.scrollTo(0, (all * nodeHight) - 100)
      } else {
        dom.scrollTo(0, 0)
      }
    },
    getCheckedImage() {
      this.onDragging({top: 0, left: 0})
      this.image_path = this.$route.query.image_path
      this.show_remote_image()
      // window.addEventListener('keydown', this.keydown)
    },
    keydown(e) {
      // if (e.which === 46 || e.which === 8) { // Del or Backspace
      //   if (!this.isDialogOpen) {
      //     // console.log('按下del')
      //     this.del_sel_regions()
      //     e.preventDefault() // 阻止默认事件，因为会导致搜索框不能删除文字，所以不阻止删除的冒泡时间
      //     return
      //   }
      // }
      // // Ctrl + 快捷键
      // if (e.ctrlKey) {
      //   this.is_ctrl_pressed = true
      //   if (e.which === 83) { // Ctrl + s
      //     this.save_data()
      //     e.preventDefault()
      //     return
      //   }
      // }
    },
    reposition() {
      this.reset_zoom_level()
      this.onDragging({top: 0, left: 0})
    },
    onDragging(newRect) {
      // this.$refs.vueDR.parentWidth = 1283
      // // this.$refs.vueDR.parentWidth = 0
      // this.$refs.vueDR.right = 1282
      // this.$refs.vueDR.Height = 0
      // this.$refs.vueDR.bottom = -1
      // console.log('width',this.$refs.vueDR.parentWidth - newRect.left - this.$refs.vueDR.right)
      // console.log('height',this.parentHeight - newRect.top - this.$refs.vueDR.bottom)
      // console.log('newRect',newRect)
      this.$refs.vueDR.top = newRect.top
      this.$refs.vueDR.left = newRect.left
    },
    throttle(fn, gapTime) {
      const _this = this
      return function () {
        const _nowTime = +new Date()
        if (_nowTime - _this._lastTime > gapTime || !_this._lastTime) {
          fn(...arguments)// 函数可以带参数
          _this._lastTime = _nowTime
        }
      }
    },
    handleWheel(e) {
      e.stopPropagation()
      this.throttle(this.pageUpOrDown, 100)(e)
    },
    pageUpOrDown(e) {
      if (e.deltaY > 0) {
        // 向下
        // this.clickNext()//执行的事件逻辑。比如向下翻页
        this.zoom_out()
      } else if (e.deltaY < 0) {
        // 向上
        this.zoom_in()// 比如向上翻页
      }
    },
    getStyle() {
      if (this.isDragActive) {
        // console.log("111111111111")
        return {cursor: 'pointer'}
      } else {
        return {}
      }
    },
    getDetectStyle(need_detect) {
      // console.log('2222222222222',need_detect)
      if (need_detect === 0) {
        return {width: '100%', color: 'blue'}
      } else {
        return {width: '100%'}
      }
    },
    /*
    初始化
    * */
    get_init() {
      this.invisible_file_input = document.getElementById('invisible_file_input')
      this.img_canvas = document.getElementById('image_canvas')
      this.img_ctx = this.img_canvas.getContext('2d')
      this.reg_canvas = document.getElementById('region_canvas')
      this.leftAll = document.getElementById('leftAll')
      this.leftTree = document.getElementById('leftTree')
      this.tree = document.getElementById('Tree')
      // console.log('leftAll.style.offsetHeight',this.leftAll.offsetHeight)
      // console.log('leftTree.style.offsetHeight',this.leftTree.offsetHeight)
      // console.log('tree.style.offsetHeight',this.tree.offsetHeight)
      this.reg_ctx = this.reg_canvas.getContext('2d')
      this.ui_top_panel = document.getElementById('ui_top_panel')
      this.canvas_panel = document.getElementById('canvas_panel') // 包裹image_canvas和region_canvas的区域
      this.canvas_zoom_level_index = this.CANVAS_DEFAULT_ZOOM_LEVEL_INDEX
      this.current_shape = this.REGION_SHAPE.RECT
    },
    /*
    目录树部分
    * */
    show_node(node, data) {
      // console.log('点击')
      // console.log('node', node)  //level只是层级 1-3 parent是父节点
      const current_node_id = node.data.id
      for (let i = 0; i < this.canvas_regions.length; i++) {
        if (this.canvas_regions[i].node_id === current_node_id) {
          // console.log('找到对应的区域', this.canvas_regions[i])
          this.toggle_all_regions_selection(false)
          this.set_region_select_state(i, true)
          this.redraw_reg_canvas()
          this.reg_canvas.focus()
          break
        }
      }
    },
    get_image_id(filename, size) {
      if (typeof (size) === 'undefined') {
        return filename
      } else {
        return filename + size
      }
    },
    set_all_canvas_scale(s) {
      this.img_ctx.scale(s, s) // 只缩放以后的画图操作，会同时缩放坐标
      this.reg_ctx.scale(s, s)
    },
    set_all_canvas_size(w, h) {
      this.img_canvas.height = h
      this.img_canvas.width = w
      this.reg_canvas.height = h
      this.reg_canvas.width = w
      this.canvas_panel.style.height = h + 'px'
      this.canvas_panel.style.width = w + 'px'
    },
    // transform regions in image space to canvas space
    load_canvas_regions() {
      // load all existing annotations into canvas_regions
      const regions = this.remote_regions
      this.canvas_regions = []
      for (let i = 0; i < regions.length; ++i) {
        const regioni = new ImageRegion()
        regioni.node_id = regions[i].node_id
        for (const key of regions[i].shape_attributes.keys()) { // regions存着该图片各个框的坐标信息
          const value = regions[i].shape_attributes.get(key)
          regioni.shape_attributes.set(key, value)
        }
        this.canvas_regions.push(regioni)
        switch (this.canvas_regions[i].shape_attributes.get('name')) {
          case this.REGION_SHAPE.RECT:
            const x = regions[i].shape_attributes.get('x') / this.canvas_scale // 转化为画布大小
            const y = regions[i].shape_attributes.get('y') / this.canvas_scale
            const width = regions[i].shape_attributes.get('width') / this.canvas_scale
            const height = regions[i].shape_attributes.get('height') / this.canvas_scale
            this.canvas_regions[i].shape_attributes.set('x', Math.round(x))
            this.canvas_regions[i].shape_attributes.set('y', Math.round(y))
            this.canvas_regions[i].shape_attributes.set('width', Math.round(width))
            this.canvas_regions[i].shape_attributes.set('height', Math.round(height))
            break
        }
      }
    },
    redraw_reg_canvas() {
      if (this.current_image_loaded) {
        if (this.canvas_regions.length > 0) {
          // 清空矩形框的画布
          this.reg_ctx.clearRect(0, 0, this.reg_canvas.width, this.reg_canvas.height)
          if (this.is_region_boundary_visible) { // 画画
            this.draw_all_regions()
          }
        }
      }
    },
    draw_all_regions() {
      for (let i = 0; i < this.canvas_regions.length; ++i) {
        const attr = this.canvas_regions[i].shape_attributes // canvas_regions存的是缩放过的
        const is_selected = this.canvas_regions[i].is_user_selected
        switch (attr.get('name')) {
          case this.REGION_SHAPE.RECT:
            this.draw_rect_region(attr.get('x'),
              attr.get('y'),
              attr.get('width'),
              attr.get('height'),
              attr.get('degree'),
              is_selected)
            break
        }
      }
    },
    draw_rect_region(x, y, w, h, degree, is_selected) {
      if (is_selected) {
        let t = this.draw_rect(x, y, w, h, degree)
        let new_x = t.new_x  //转换后的新坐标
        let new_y = t.new_y
        this.reg_ctx.strokeStyle = this.THEME_SEL_REGION_FILL_BOUNDARY_COLOR
        this.reg_ctx.lineWidth = this.THEME_REGION_BOUNDARY_WIDTH / 2
        this.reg_ctx.stroke() //绘制出通过 moveTo() 和 lineTo() 方法定义的路径
        this.reg_ctx.fillStyle = this.THEME_SEL_REGION_FILL_COLOR
        this.reg_ctx.globalAlpha = this.THEME_SEL_REGION_OPACITY //透明度
        this.reg_ctx.fill()
        this.reg_ctx.globalAlpha = 1.0  //不透明
        //画边角四个点
        this.draw_control_point(new_x[0], new_y[0])
        this.draw_control_point(new_x[1], new_y[1])
        this.draw_control_point(new_x[2], new_y[2])
        this.draw_control_point(new_x[3], new_y[3])
      } else {
        // draw a fill line
        this.reg_ctx.strokeStyle = this.THEME_BOUNDARY_FILL_COLOR
        this.reg_ctx.lineWidth = this.THEME_REGION_BOUNDARY_WIDTH
        // this.reg_ctx.lineWidth = this.THEME_REGION_BOUNDARY_WIDTH / 2
        this.draw_rect(x, y, w, h, degree)
        this.reg_ctx.stroke()
      }
    },
    // control point for resize of region boundaries
    draw_control_point(cx, cy) {
      this.reg_ctx.beginPath()
      this.reg_ctx.arc(cx, cy, this.REGION_POINT_RADIUS, 0, 2 * Math.PI, false)
      this.reg_ctx.closePath()
      this.reg_ctx.fillStyle = this.THEME_CONTROL_POINT_COLOR
      this.reg_ctx.globalAlpha = 1.0
      this.reg_ctx.fill()
    },
    draw_rect(x, y, w, h, degree) {
      if (degree === 0) {
        this.reg_ctx.beginPath()
        this.reg_ctx.moveTo(x, y)
        this.reg_ctx.lineTo(x + w, y)
        this.reg_ctx.lineTo(x + w, y + h)
        this.reg_ctx.lineTo(x, y + h)
        this.reg_ctx.closePath()//闭环,从当前点到开始点的路径
        let new_x = [x, x + w, x + w, x]
        let new_y = [y, y, y + h, y + h]
        return {
          new_x: new_x,
          new_y: new_y
        }
      } else {
        let c_x = x + w / 2
        let c_y = y + h / 2
        let t = this.compulte_new_xy(x, y, w, h, degree, c_x, c_y)
        let new_x = t.new_x
        let new_y = t.new_y
        this.reg_ctx.beginPath()
        this.reg_ctx.moveTo(new_x[0], new_y[0])
        this.reg_ctx.lineTo(new_x[1], new_y[1])
        this.reg_ctx.lineTo(new_x[2], new_y[2])
        this.reg_ctx.lineTo(new_x[3], new_y[3])
        this.reg_ctx.closePath()//闭环,从当前点到开始点的路径
        return {
          new_x: new_x,
          new_y: new_y
        }
      }
    },
    zoom_in() { // 放大
      if (!this.current_image_loaded) {
        // console.log('请先加载图片,First load some images!')
        return
      }
      if (this.canvas_zoom_level_index === (this.CANVAS_ZOOM_LEVELS.length - 1)) {
        // console.log('Further zoom-in not possible')
      } else {
        this.canvas_zoom_level_index += 1
        this.is_canvas_zoomed = true
        const zoom_scale = this.CANVAS_ZOOM_LEVELS[this.canvas_zoom_level_index]
        this.set_all_canvas_scale(zoom_scale)
        this.set_all_canvas_size(this.canvas_width * zoom_scale,
          this.canvas_height * zoom_scale)
        this.canvas_scale = this.canvas_scale_without_zoom / zoom_scale

        this.load_canvas_regions() // image to canvas space transform
        this.redraw_img_canvas()
        this.redraw_reg_canvas()
        this.reg_canvas.focus()
        // console.log('Zoomed in to level ' + zoom_scale + 'X')
      }
    },
    zoom_out() { // 缩小
      if (!this.current_image_loaded) {
        // console.log('请先加载图片,First load some images!')
        return
      }
      if (this.canvas_zoom_level_index === 0) { // 不能再缩小了
        // console.log('Further zoom-out not possible')
      } else {
        this.canvas_zoom_level_index -= 1
        this.is_canvas_zoomed = true
        const zoom_scale = this.CANVAS_ZOOM_LEVELS[this.canvas_zoom_level_index]
        this.set_all_canvas_scale(zoom_scale)
        this.set_all_canvas_size(this.canvas_width * zoom_scale,
          this.canvas_height * zoom_scale) // 缩放画布，但是会把图片清空
        this.canvas_scale = this.canvas_scale_without_zoom / zoom_scale

        this.load_canvas_regions() // 加载当前图片的标注框image to canvas space transform
        this.redraw_img_canvas() // 画图片
        this.redraw_reg_canvas() // 画矩形框
        this.reg_canvas.focus()
        // console.log('Zoomed out to level ' + zoom_scale + 'X')
      }
    },
    reset_zoom_level() {
      if (!this.current_image_loaded) {
        // console.log('First load some images!')
        return
      }
      if (this.is_canvas_zoomed) {
        this.is_canvas_zoomed = false
        this.canvas_zoom_level_index = this.CANVAS_DEFAULT_ZOOM_LEVEL_INDEX
        const zoom_scale = this.CANVAS_ZOOM_LEVELS[this.canvas_zoom_level_index]
        this.set_all_canvas_scale(zoom_scale)
        this.set_all_canvas_size(this.canvas_width, this.canvas_height)
        this.canvas_scale = this.canvas_scale_without_zoom

        this.load_canvas_regions() // image to canvas space transform
        this.redraw_img_canvas()
        this.redraw_reg_canvas()
        this.reg_canvas.focus()
        // console.log('Zoom reset')
      } else {
        // console.log('Cannot reset zoom because image zoom has not been applied!')
      }
    },
    redraw_img_canvas() {
      if (this.current_image_loaded) {
        this.img_ctx.clearRect(0, 0, this.img_canvas.width, this.img_canvas.height)
        this.img_ctx.drawImage(this.current_image, 0, 0,
          this.img_canvas.width, this.img_canvas.height)
      }
    },
    is_on_region_corner(px, py) {
      const region_edge = [-1, -1] // region_id, corner_id [top-left=1,top-right=2,bottom-right=3,bottom-left=4]
      for (let i = 0; i < this.canvas_regions.length; ++i) { // canvas_regions存着当前图片的框坐标，非原图尺度
        const attr = this.canvas_regions[i].shape_attributes
        let result = false
        region_edge[0] = i
        switch (attr.get('name')) {
          case this.REGION_SHAPE.RECT:
            result = this.is_on_rect_edge(attr.get('x'),
              attr.get('y'),
              attr.get('width'),
              attr.get('height'),
              attr.get('degree'),
              px, py)
            break
        }
        if (result > 0) { // 电机在框i的边缘上
          region_edge[1] = result
          return region_edge
        }
      }
      region_edge[0] = -1
      return region_edge
    },
    is_on_rect_edge(x, y, w, h, degree, px, py) {
      if (degree === 0) {
        let dx0 = Math.abs(x - px)
        let dy0 = Math.abs(y - py)
        let dx1 = Math.abs(x + w - px)
        let dy1 = Math.abs(y + h - py)
        //[top-left=1,top-right=2,bottom-right=3,bottom-left=4]
        if (dx0 < this.REGION_EDGE_TOL &&
          dy0 < this.REGION_EDGE_TOL) {
          return 1
        }
        if (dx1 < this.REGION_EDGE_TOL &&
          dy0 < this.REGION_EDGE_TOL) {
          return 2
        }
        if (dx1 < this.REGION_EDGE_TOL &&
          dy1 < this.REGION_EDGE_TOL) {
          return 3
        }
        if (dx0 < this.REGION_EDGE_TOL &&
          dy1 < this.REGION_EDGE_TOL) {
          return 4
        }
        return 0
      } else {
        let c_x = x + w / 2
        let c_y = y + h / 2
        let t = this.compulte_new_xy(x, y, w, h, degree, c_x, c_y)
        let new_x = t.new_x
        let new_y = t.new_y
        for (let i = 0; i < new_x.length; i++) {
          let dx = Math.abs(new_x[i] - px)
          let dy = Math.abs(new_y[i] - py)
          if (dx < this.REGION_EDGE_TOL &&
            dy < this.REGION_EDGE_TOL) {
            return i + 1
          }
        }
        return 0
      }
    },
    is_inside_region(px, py, descending_order) {
      const N = this.canvas_regions.length
      if (N === 0) {
        return -1
      }
      let start, end, del
      // traverse the canvas regions in alternating ascending
      // and descending order to solve the issue of nested regions
      if (descending_order) {
        start = N - 1
        end = -1
        del = -1
      } else {
        start = 0
        end = N
        del = 1
      }
      let i = start
      while (i !== end) {
        const yes = this.is_inside_this_region(px, py, i)
        if (yes) {
          return i
        }
        i = i + del
      }
      return -1
    },
    is_inside_this_region(px, py, region_id) {
      const attr = this.canvas_regions[region_id].shape_attributes
      let result = false
      switch (attr.get('name')) {
        case this.REGION_SHAPE.RECT:
          result = this.is_inside_rect(attr.get('x'),
            attr.get('y'),
            attr.get('width'),
            attr.get('height'),
            attr.get('degree'),
            px, py)
          break
      }
      return result
    },
    is_inside_rect(x, y, w, h, degree, px, py) {
      if (degree === 0) {
        return px > x &&
          px < (x + w) &&
          py > y &&
          py < (y + h)
      } else {
        let c_x = x + w / 2  //旋转中心
        let c_y = y + h / 2
        let t = this.compulte_new_xy(x, y, w, h, degree, c_x, c_y)  //绕着矩形中心旋转degree度
        let all_points_x = t.new_x
        let all_points_y = t.new_y
        all_points_x.push(t.new_x[0])  //形成一个闭环
        all_points_y.push(t.new_y[0])
        let wn = 0    // the  winding number counter
        // loop through all edges of the polygon
        for (let i = 0; i < all_points_x.length - 1; i++) {   // edge from V[i] to  V[i+1]
          let is_left_value = this.is_left(all_points_x[i], all_points_y[i],
            all_points_x[i + 1], all_points_y[i + 1],
            px, py)
          if (all_points_y[i] <= py) {
            if (all_points_y[i + 1] > py && is_left_value > 0) {  // 线是从下到上,点击点在线的左边
              ++wn
            }
          } else {
            if (all_points_y[i + 1] <= py && is_left_value < 0) {  //线是从上到下,点击点在线的右边
              --wn
            }
          }
        }
        return wn !== 0
      }
    },
    // returns
    // >0 if (x2,y2) lies on the left side of line joining (x0,y0) and (x1,y1)
    // =0 if (x2,y2) lies on the line joining (x0,y0) and (x1,y1)
    // >0 if (x2,y2) lies on the right side of line joining (x0,y0) and (x1,y1)
    // source: http://geomalgorithms.com/a03-_inclusion.html
    is_left(x0, y0, x1, y1, x2, y2) {
      return (((x1 - x0) * (y2 - y0)) - ((x2 - x0) * (y1 - y0)))
    },
    toggle_all_regions_selection(is_selected) {
      for (let i = 0; i < this.canvas_regions.length; ++i) {
        this.canvas_regions[i].is_user_selected = is_selected // 当前图片的所有region都清空
      }
      this.is_all_region_selected = is_selected
    },
    rect_update_corner(corner_id, d, x, y) {
      // pre-condition : d[x0,y0,x1,y1] is standarized
      // post-condition : corner is moved ( d may not stay standarized )
      switch (corner_id) {
        case 1: // top-left
          d[0] = x
          d[1] = y
          break
        case 3: // bottom-right
          d[2] = x
          d[3] = y
          break
        case 2: // top-right
          d[2] = x
          d[1] = y
          break
        case 4: // bottom-left
          d[0] = x
          d[3] = y
          break
      }
    },
    rect_standarize_coordinates(d) {
      // d[x0,y0,x1,y1]
      // ensures that (d[0],d[1]) is top-left corner while
      // (d[2],d[3]) is bottom-right corner
      if (d[0] > d[2]) {
        // swap
        const t = d[0]
        d[0] = d[2]
        d[2] = t
      }

      if (d[1] > d[3]) {
        // swap
        const t = d[1]
        d[1] = d[3]
        d[3] = t
      }
    },
    set_region_select_state(region_id, is_selected) {
      this.canvas_regions[region_id].is_user_selected = is_selected
    },
    clear_reg_canvas() {
      this.reg_ctx.clearRect(0, 0, this.reg_canvas.width, this.reg_canvas.height)
    },
    openMessageBox(message) {
      this.$alert(message, ' ', {
        confirmButtonText: '确定'
        // callback: action => {
        //   this.$message({
        //     type: 'info',
        //     message: `action: ${ action }`
        //   });
        // }
      })
    },
    show_remote_image() { // 展示图片 image_index:图片列表的index
      this.reposition()
      if (this.is_loading_current_image) {
        // console.log('请等当前图片加载完再点击')
        return
      }
      const img_reader = new FileReader()
      console.log('正在加载图片')
      this.is_loading_current_image = true // 正在加载图片
      const that = this
      img_reader.addEventListener('loadstart', function (e) {
      }, false)
      img_reader.addEventListener('progress', function (e) {
      }, false)
      img_reader.addEventListener('error', function () {
        that.is_loading_current_image = false
        // console.log('加载图片出错' + img_filename + ' !')
      }, false)
      img_reader.addEventListener('abort', function () {
        that.is_loading_current_image = false
        // console.log('Aborted loading image ' + img_filename + ' !')
      }, false)
      img_reader.addEventListener('load', function () {
        // // console.log('1234345436')
        that.current_image = new Image()
        that.current_image.addEventListener('error', function () {
          that.is_loading_current_image = false
          // console.log('Error loading image ' + img_filename + ' !')
        }, false)
        that.current_image.addEventListener('abort', function () {
          that.is_loading_current_image = false
          // console.log('Aborted loading image ' + img_filename + ' !')
        }, false)
        that.current_image.addEventListener('load', function () {
          // update the current state of application
          that.current_image_loaded = true // 图片加载成功
          that.is_loading_current_image = false // 目前不在加载图片
          that.is_window_resized = false
          that.is_region_selected = false
          that.user_sel_region_id = -1
          that.current_image_width = that.current_image.naturalWidth // 图片真实的宽高
          that.current_image_height = that.current_image.naturalHeight
          // set the size of canvas
          // based on the current dimension of browser window
          const de = document.documentElement
          const canvas_panel_width = de.clientWidth - 230 // 宽度-侧边栏的宽度 页面用于显示图像部分的宽高
          const canvas_panel_height = de.clientHeight - 2 * that.ui_top_panel.offsetHeight// 高度-2倍顶栏
          that.canvas_width = that.current_image_width // 真实图片宽度
          that.canvas_height = that.current_image_height
          if (that.canvas_width > canvas_panel_width) { // 如果原图像的大小>页面用于显示图片区域的大小
            // resize image to match the panel width
            const scale_width = canvas_panel_width / that.current_image.naturalWidth
            that.canvas_width = canvas_panel_width // resize
            that.canvas_height = that.current_image.naturalHeight * scale_width
          }
          if (that.canvas_height > canvas_panel_height) {
            // resize further image if its height is larger than the image panel
            const scale_height = canvas_panel_height / that.canvas_height
            that.canvas_height = canvas_panel_height
            that.canvas_width = that.canvas_width * scale_height
          }
          that.canvas_width = Math.round(that.canvas_width)
          that.canvas_height = Math.round(that.canvas_height)
          that.canvas_scale = that.current_image.naturalWidth / that.canvas_width
          that.canvas_scale_without_zoom = that.canvas_scale // 缩放比例
          that.set_all_canvas_size(that.canvas_width, that.canvas_height) // resize img_region和cav_region

          // we only need to draw the image once in the image_canvas 在image_canvas上画一次原图
          that.img_ctx.clearRect(0, 0, that.canvas_width, that.canvas_height) // 清空一定区域内的像素
          that.img_ctx.drawImage(that.current_image, 0, 0, that.canvas_width, that.canvas_height)

          // 加载目录树，如果本地该图片的目录树不为空，则加载本地
          // 如果本地为空，远程端目录树不为'none'，则加载远程的node_data和is_vetical
          that.max_id = 0 // 每一张图片都有自己的从0开始的id编号，由于可能会删除区域，所以不可能连续，所以要获得最大的id，方便接下来计数
          if (that.remote_node_data !== 'none') {
            console.log('远程目录树数据不为空，加载远程目录树数据')
            that.node_data = that.remote_node_data // 加载远程的目录树信息
            that.isVetical = that.remote_is_vetical // 加载远程端的is_vetical信息
            that.remote_regions = []
            for (let i = 0; i < that.node_data.length; i++) {
              that.load_remote_data(that.node_data[i]) // 把node_data写入regions
              that.get_max_id(that.node_data[i]) // 获取目录树当前最大id
            }
          } else {
            console.log('当前图片的目录树信息为空')
          }
          // console.log('当前node_data的最大id', that.max_id)
          that.nid = that.max_id + 1 // 用来计数
          that.load_canvas_regions() // 将img_meta中的regions加载到canvas层面
          that.redraw_reg_canvas()
          that.reg_canvas.focus() // 焦点设置在regcav上，以便画图
        })
        // // console.log('img_reader.result', img_reader.result)
        that.current_image.src = img_reader.result
      }, false)
      new Promise((resolve, reject) => {
        resolve(getImageShow(this.image_path))// 请求img的地址
      }).then((res) => {
        that.base64_img_data = res.data.imageBase64
        that.remote_node_data = res.data.node_data // 获取远程的nodedata，如果为'none',则说明远程端没有该文件的标注数据
        that.remote_is_vetical = res.data.is_vetical // 获取远程的is_vetical状态
        that.image_name = res.data.image_name
        that.image_status = res.data.image_status
        that.num_box = res.data.num_box
        that.task_name = res.data.task_name
        that.user_name = res.data.user_name
        img_reader.readAsText(new Blob([that.base64_img_data]))
      })
    },
    compulte_new_xy(x, y, w, h, degree, c_x, c_y) {
      let old_x = [x, x + w, x + w, x]
      let old_y = [y, y, y + h, y + h]
      let new_x = []
      let new_y = []
      for (let i = 0; i < old_x.length; i++) {  //矩形四角绕中心点旋转degree度
        new_x.push(Math.round((old_x[i] - c_x) * Math.cos(degree * (Math.PI / 180)) - (old_y[i] - c_y) * Math.sin(degree * (Math.PI / 180)) + c_x))
        new_y.push(Math.round((old_x[i] - c_x) * Math.sin(degree * (Math.PI / 180)) + (old_y[i] - c_y) * Math.cos(degree * (Math.PI / 180)) + c_y))
      }
      return {
        new_x: new_x,
        new_y: new_y
      }
    },
    get_max_id(n_data) { // 获得目录树的最大id值
      if (n_data['id'] > this.max_id) {
        this.max_id = n_data['id']
      }
      for (let i = 0; i < n_data['children'].length; i++) {
        this.get_max_id(n_data['children'][i])
      }
    },
    load_remote_data(n_data) { // 遍历加载regions
      // // console.log('child', n_data['children'])
      if (n_data['xy'].length !== 0) {
        const x = Math.round(n_data['xy'][0]) // 实际坐标
        const y = Math.round(n_data['xy'][1])
        const width = Math.round(n_data['xy'][2] - n_data['xy'][0])
        const height = Math.round(n_data['xy'][3] - n_data['xy'][1])
        let degree = 0
        if (n_data.hasOwnProperty('degree')) {
          degree = n_data['degree']  //倾斜角(非弧度)，倾斜角不为0的时候，xy的坐标经过倾斜角转换才是真实框的坐标
        } else {
          n_data['degree'] = 0
        }
        const img_region = new ImageRegion()
        img_region.node_id = n_data['id']
        img_region.shape_attributes.set('name', 'rect')
        img_region.shape_attributes.set('degree', degree)  //角度而非弧度
        img_region.shape_attributes.set('x', x)
        img_region.shape_attributes.set('y', y)
        img_region.shape_attributes.set('width', width)
        img_region.shape_attributes.set('height', height)
        this.remote_regions.push(img_region)
      }
      for (let i = 0; i < n_data['children'].length; i++) {
        this.load_remote_data(n_data['children'][i])
      }
    },
    go_back() {
      // this.$router.push({
      //   path: 'taskMark'
      // })
      // this.$store.dispatch('taskcheck', this.$route);
      this.$router.replace('taskMark')
      // this.$router.go(-1);
    }
  }
}

</script>

<style scoped>
body {
  min-width: 800px;
  padding: 0;
  margin: 0;
  font-family: "Microsoft YaHei";
}

body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, button, textarea, p, blockquote, table, th, td {
  margin: 0;
  padding: 0;
}

.top_panel {
  position: fixed;
  display: block;
  font-size: medium;
  background-color: #42b983;
  color: whitesmoke;
  z-index: 10;
  margin: 0;
  padding: 0;
  width: 100%;
}

.navbar {
  display: right;
}

.navbar ul {
  display: inline;
  list-style-type: none;
  overflow: hidden;
}

.navbar li {
  float: left;
}

.navbar li a, .drop_menu_item {
  display: inline-block;
  color: whitesmoke;
  padding: 0.65em 1.2em;
  text-decoration: none;
}

.navbar li a:hover, .dropdown:hover {
  background-color: #42b983;
  cursor: pointer;
}

.navbar li.dropdown {
  display: inline-block;
}

.navbar .dropdown-content {
  display: none;
  position: absolute;
  background-color: #42b983;
  min-width: 120px;
  border: 1px solid #ffffff;
  font-size: small;
}

.navbar .dropdown-content a {
  color: whitesmoke;
  padding: 0.4em 0.6em;
  text-decoration: none;
  display: block;
  text-align: left;
  background-color: #42b983;
  float: none;
}

.navbar .dropdown-content a:hover {
  background-color: #FFC000;
  color: whitesmoke;
}

.navbar .dropdown:hover .dropdown-content {
  display: block;
}

.toolbar {
  display: inline-block;
  color: white;
  vertical-align: top;
}

.toolbar ul {
  display: inline;
  list-style-type: none;
  overflow: hidden;
}

.toolbar li {
  font-size: medium;
  float: left;
  padding: 0.65em 0.3em;
  color: whitesmoke;
}

.toolbar li:hover {
  background-color: #FFC000;
  color: black;
  cursor: pointer;
}

#fileinfo {
  font-size: small;
  padding: 1.2em 0.8em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Middle panel: containing #image_panel, #leftsidebar */
.middle_panel {
  position: relative;
  display: table;
  table-layout: fixed;
  width: 100%;
  z-index: 1;
  padding: 0;
  top: 3.5em;
  /*padding-top: 1.0125em;  ensures the mouse event (x,y) coordinates are integer */
}

#leftsidebar {
  display: table-cell;
  width: 250px;
  z-index: 10;
  vertical-align: top;
}

#display_area {
  display: table-cell;
  width: 100%;
  z-index: 1;
  margin: 0;
  padding-left: 1em;
  vertical-align: top;
}

#canvas_panel {
  position: relative;
  margin: 0;
  padding: 0;
}

#leftsidebar_collapse_panel {
  display: table-cell;
  position: relative;
  width: 10px;
  z-index: 1;
  vertical-align: top;
  font-size: small;
}

#leftsidebar_collapse_button {
  background-color: #42b983;
  width: 10px;
  height: 25px;
  color: whitesmoke;
  padding: 0.2em;
  border-radius: 0px 5px 5px 0px;
  font-size: large;
}

#leftsidebar_collapse_button:hover {
  color: white;
  cursor: pointer;
}

/* Left sidebar accordion */
button.leftsidebar_accordion {
  font-size: large;
  background-color: #f2f2f2;
  cursor: pointer;
  padding: 0.5em 0.5em;
  width: 100%;
  text-align: left;
  border: 0;
  outline: none;
}

button.leftsidebar_accordion:focus {
  outline: none;
}

button.leftsidebar_accordion.active, button.leftsidebar_accordion:hover {
  background-color: #e6e6e6;
}

button.leftsidebar_accordion:after {
  content: '\02795';
  color: #4d4d4d;
  float: right;
}

button.leftsidebar_accordion.active:after {
  content: '\2796';
}

.leftsidebar_accordion_panel {
  display: none;
  padding-top: 0;
  padding-left: 0.5em;
  font-size: small;
  border-right: 2px solid #f2f2f2;
  border-bottom: 2px solid #f2f2f2;
}

.leftsidebar_accordion_panel.show {
  display: block;
}

/* Region shape selection panel inside leftsidebar */
ul.region_shape {
  font-size: xx-large;
  list-style-type: none;
  overflow: hidden;
  padding: 0.4em 0;
  margin: 0;
}

ul.region_shape li {
  float: left;
  padding: 0 0.2em;
  fill: #ffffff;
  stroke: #000000;
}

ul.region_shape li:hover {
  cursor: pointer;
  fill: #ffffff;
  stroke: #ff0000;
}

ul.region_shape .selected {
  fill: #ffffff;
  stroke: #ff0000;
}

/* Loaded image list shown in leftsidebar panel */
#img_list_panel {
  display: none;
  height: 0;
  font-size: small;
  overflow: scroll;
}

#img_list_panel ul {
  position: relative;
  line-height: 1.3em;
  padding-left: 0;
  list-style-type: none;
}

#img_list_panel li {
  white-space: nowrap;
}

#img_list_panel li:hover {
  background-color: #cccccc;
  color: #000000;
  cursor: pointer;
}

#message_panel {
  position: fixed;
  left: 0;
  bottom: 0px;
  line-height: 3em;
  width: 100%;
  background-color: #00a9cf;
  color: black;
  font-size: small;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  z-index: 1000;
}

#invisible_file_input {
  width: 0.1px;
  height: 0.1px;
  opacity: 0;
  overflow: hidden;
  position: absolute;
  z-index: -1;
}

.text_panel {
  display: none;
  margin: auto;
  font-size: medium;
  line-height: 1.3em;
  margin: 0;
  max-width: 700px;
}

.text_panel li {
  margin: 1em 0;
  text-align: left;
}

.text_panel p {
  text-align: left;
}

.action_text_link {
  background-color: #aaeeff;
  color: #000000;
}

.action_text_link:hover {
  cursor: pointer;
}

.svg_button:hover {
  cursor: pointer;
}

.tool_button {
  color: blue;
  cursor: pointer;
}

.tool_button:hover {
  color: red;
}

/* region and file attributes input panel (spreadsheet like) */
#attributes_panel {
  display: none;
  position: fixed;
  bottom: 0;
  z-index: 10;
  width: 100%;
  max-height: 30%;
  overflow: auto;
  background-color: #ffffff;
  border-top: 4px solid #000000;
  padding: 0em 0em;
  padding-bottom: 2em;
  font-size: small;
}

#attributes_panel table {
  border-collapse: collapse;
  table-layout: fixed;
  margin: 1em;
  margin-bottom: 2em;
}

#attributes_panel td {
  border: 1px solid #999999;
  padding: 1em 1em;
  margin: 0;
  height: 1em;
  white-space: nowrap;
  vertical-align: top;
}

#attributes_panel tr:first-child td, #attributes_panel td:first-child {
  padding: 1em 1em;
  text-align: center;
}

#attributes_panel input {
  border: none;
  padding: 0;
  margin: 0;
  display: table-cell;
  height: 1.3em;
  font-size: small;
  background-color: #ffffff;
  vertical-align: top;
}

#attributes_panel input:hover {
  background-color: #e6e6e6;
}

#attributes_panel input:focus {
  background-color: #e6e6e6;
}

#attributes_panel input:not(:focus) {
  text-align: center;
}

#attributes_panel textarea {
  border: none;
  padding: 0;
  margin: 0;
  display: table-cell;
  font-size: small;
  background-color: #ffffff;
}

#attributes_panel textarea:hover {
  background-color: #e6e6e6;
}

#attributes_panel textarea:focus {
  background-color: #e6e6e6;
}

#attributes_panel_toolbar {
  display: block;
  height: 30px;
  width: 100%;
  position: relative;
  padding: 0;
  margin: 0;
}

.attributes_panel_button {
  width: 10px;
  color: black;
  font-size: x-large;
  margin-left: 0.5em;
  padding: 0;
}

.attributes_panel_button:hover {
  color: red;
  cursor: pointer;
}

/* layers of canvas */
#image_panel {
  position: relative;
  display: inline-block;
  margin: auto;
  margin-top: 1em;
}

#image_canvas {
  position: absolute;
  border: 2px solid #d71b1b;
  top: 0px;
  left: 0px;
  z-index: 1;
}

#region_canvas {
  position: absolute;
  top: 0px;
  left: 0px;
  z-index: 2;
}

/* Loading spinbar */
.loading_spinbox {
  display: inline-block;
  border: 0.4em solid #cccccc;
  border-radius: 50%;
  border-top: 0.4em solid #000000;
  -webkit-animation: spin 2s linear infinite;
  animation: spin 2s linear infinite;
}

@-webkit-keyframes spin {
  0% {
    -webkit-transform: rotate(0deg);
  }

  100% {
    -webkit-transform: rotate(360deg);
  }
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.text {
  font-size: 14px;
}

li {
  list-style: none;
}

.item {
  margin-bottom: 6px;
}

.clearfix:before,
.clearfix:after {
  display: table;
  content: "";
}

.clearfix:after {
  clear: both
}

.box-card {
  width: auto;
  /*padding-left: 5px;*/
}

.el-dropdown-link {
  cursor: pointer;
  color: #409EFF;
}

.el-icon-arrow-down {
  font-size: 12px;
}

.demonstration {
  display: block;
  color: #8492a6;
  font-size: 14px;
  margin-bottom: 20px;
}

.custom-class .menu_item__available:hover {
  background: #ffecf2 !important;
  color: #ff4050 !important;
}

.leftSizeBar {
  position: fixed;
}

.box-card {
  height: 100%;
  /*padding: 0;*/
}

.tree_box {
  max-height: 400px;
  overflow: auto;
}

.box {
  max-height: 200px;
  overflow: auto;
}

.explorerBox {
  height: 390px;
  overflow: auto;
}

.el-switch__label {
  position: absolute;
  display: none;
  font-size: 11px !important;
  color: #fff !important;
}

.el-switch__label * {
  font-size: 11px !important;
}

/*打开时文字位置设置*/
.el-switch__label--right {
  z-index: 1;
  right: -10px;
  top: 0.5px;
}

/*关闭时文字位置设置*/
.el-switch__label--left {
  z-index: 1;
  left: 23px;
  top: 0.5px;
}

/*显示文字*/
.el-switch__label.is-active {
  display: block;
}

.el-switch__core {
  width: 45px;
  height: 22px;
  border: 2px solid #DCDFE6;
  border-radius: 13px;
}

.active {
  color: red;
}

.btnClass {
  font-size: 16px;
  margin-right: 30px;
  margin-left: 30px;
}

.mainSizeBar {
  margin-left: 250px;
}

/* >>> .el-tree-node:focus > .el-tree-node__content {
  background-color：rgb(3, 35, 68);
  } */
/* .warpper .el-tree--highlight-current >>> .el-tree-node.is-checked > .el-tree-node__content {
    background-color: rgb(255, 255, 255);
    color:rgb(64, 158, 255);
  }
 .warpper .el-tree--highlight-current >>> .el-tree-node.is-current > .el-tree-node__content {
    background-color: rgb(212, 107, 107);
    color:rgb(3, 35, 68);
  } */
</style>
